package com.dbsop.common.util;

import com.dbsop.anthorizatiion.ESAnalyzer;
import com.dbsop.anthorizatiion.ESDocument;
import com.dbsop.anthorizatiion.ESFiledMapping;
import com.dbsop.anthorizatiion.ESId;
import com.dbsop.common.enums.AnalyzerEnums;
import com.dbsop.common.enums.TypeEnum;
import com.dbsop.domain.info.EsFiledInfo;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;

public class EsFieldUtils {

    /**
     * 名称映射
     * @param field
     * @return
     */
    public static String getFileMappingName(Field field){
        ESFiledMapping annotation = field.getAnnotation(ESFiledMapping.class);
        if (annotation != null && annotation.columnName() != null && !annotation.columnName().equals("")){
            return annotation.columnName();
        }
        return field.getName();
    }

    /**
     * 字段类型
     * @param field
     * @return
     */
    public static String getFieldType(Field field){
        ESFiledMapping annotation = field.getAnnotation(ESFiledMapping.class);
        if (annotation != null && !annotation.columnType().equals("")) {
            return annotation.columnType();
        }
        TypeEnum typeEnumByClass = TypeEnum.getTypeEnumByClass(field.getType());
        if (typeEnumByClass == null){
            return null;
        }
        return TypeEnum.getTypeEnumByClass(field.getType()).getEsType();
    }

    /**
     * 分词类型
     * @param field
     * @return
     */
    public static String getFieldAnalyzer(Field field){
        ESAnalyzer annotation = field.getAnnotation(ESAnalyzer.class);
        if (annotation !=null) return annotation.analyzerEnums();
        return null;
    }

    /**
     * 获取ID名称
     * @param clazz
     * @return
     */
    public static String getIdName(Class clazz){
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            if (field.isAnnotationPresent(ESId.class)) return field.getName();
        }
        return null;
    }

    /**
     * 获取索引名称
     * @param clazz
     * @return
     */
    public static String indexName(Class clazz){
        Annotation[] annotations = clazz.getAnnotations();
        if (annotations == null || annotations.length == 0) return null;
        if (!clazz.isAnnotationPresent(ESDocument.class)) return null;
        Annotation annotation = clazz.getAnnotation(ESDocument.class);
        try {
            Method index_name = annotation.annotationType().getDeclaredMethod("index_name");
            return (String) index_name.invoke(annotation);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }



    public static Integer replicas(Class clazz) {
        Annotation[] annotations = clazz.getAnnotations();
        if (annotations == null || annotations.length == 0) return null;
        if (!clazz.isAnnotationPresent(ESDocument.class)) return null;
        Annotation annotation = clazz.getAnnotation(ESDocument.class);
        try {
            Method index_name = annotation.annotationType().getDeclaredMethod("replicas");
            return (int) index_name.invoke(annotation);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }



    public static String[] indices(Class clazz) {
        Annotation[] annotations = clazz.getAnnotations();
        if (annotations == null || annotations.length == 0) return null;
        if (!clazz.isAnnotationPresent(ESDocument.class)) return null;
        Annotation annotation = clazz.getAnnotation(ESDocument.class);
        try {
            Method index_name = annotation.annotationType().getDeclaredMethod("indices");
            return  (String[]) index_name.invoke(annotation);

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 读取集成扩展的属性
     * @param clazz
     * @return
     */
    public static boolean isExt(Class clazz) {
        Annotation[] annotations = clazz.getAnnotations();
        if (annotations == null || annotations.length == 0) return false;
        if (!clazz.isAnnotationPresent(ESDocument.class)) return false;
        Annotation annotation = clazz.getAnnotation(ESDocument.class);
        try {
            Method index_name = annotation.annotationType().getDeclaredMethod("isExt");
            return (Boolean) index_name.invoke(annotation);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return false;
    }


    public static Map<String,Field> pareFieldMapping(Class clazz){
        List<Field> fieldListSupper = FieldUtils.getFieldListSupper(clazz);
        return pareFieldMapping(fieldListSupper);
    }


    public static Map<String,Field> pareFieldMapping(List<Field> fields){
        Map<String,Field> fieldMap = new HashMap<String,Field>();
        for (Field field : fields) {
            if (field.getName().equals("serialVersionUID"))continue;
            String esName = getFileMappingName(field);
            fieldMap.put(esName,field);
        }
        return fieldMap;
    }



    public static Map<String,EsFiledInfo> pareFieldEsInfo(Class clazz){
        List<EsFiledInfo> esFiledInfos = pareFieldInfo(clazz,null);
        Map<String,EsFiledInfo>  filedInfoMap = new LinkedHashMap<String, EsFiledInfo>();
        for (EsFiledInfo esFiledInfo : esFiledInfos) {
            filedInfoMap.put(esFiledInfo.getEsFieldName(),esFiledInfo);
        }
        return filedInfoMap;
    }
    /**
     * 解析class 字段信息
     * @param entity
     */
    public static List<EsFiledInfo> pareFieldInfo(Class entity,List<EsFiledInfo> esFiledInfoArr) {
        Map<String, Field> infoMap = pareFieldMapping(entity);
        return filedInfoList(infoMap.values(),esFiledInfoArr);
    }

    public static List<EsFiledInfo> filedInfoList(Collection<Field> fieldList,List<EsFiledInfo> esFiledInfoArr){
        List<EsFiledInfo> esFiledInfos = new ArrayList<EsFiledInfo>(fieldList.size());
        for (Field field : fieldList) {
            if (field.getName().equals("serialPersistentFields")){
                continue;
            }
            esFiledInfos.add(fieldInfo(field,esFiledInfoArr));
        }
        return esFiledInfos;
    }


    public static EsFiledInfo fieldInfo(Field field, List<EsFiledInfo> esFiledInfoArr){
        EsFiledInfo esFiledInfo = new EsFiledInfo();
        esFiledInfo.setEsFieldName(getFileMappingName(field));
        esFiledInfo.setEsFieldType(getFieldType(field));
        esFiledInfo.setField(field);
        esFiledInfo.setFieldName(field.getName());
        esFiledInfo.setFieldType(field.getType());
        String fullFieldName = EsStringUtils.joinObj("fieldName", esFiledInfoArr, ".");
        fullFieldName = fullFieldName == null?esFiledInfo.getFieldName():fullFieldName+"."+esFiledInfo.getFieldName();
        esFiledInfo.setFullName(fullFieldName);
        String esFullFieldName = EsStringUtils.joinObj("esFieldName", esFiledInfoArr, ".");
        esFullFieldName = esFullFieldName == null?esFiledInfo.getEsFieldName():esFullFieldName+"."+esFiledInfo.getEsFieldName();
        esFiledInfo.setFullEsFieldName(esFullFieldName);
        esFiledInfo.setAnalyzer(getFieldAnalyzer(field));
        if(Map.class.isAssignableFrom(esFiledInfo.getFieldType())){// 先判断map 类型
            esFiledInfo.setEsFieldType("object");
        }else if (Collection.class .isAssignableFrom(esFiledInfo.getFieldType())){  // 再判断list 类型
            esFiledInfoArr = esFiledInfoArr == null ? new ArrayList<EsFiledInfo>():esFiledInfoArr;
            esFiledInfoArr.add(esFiledInfo);
            typeList(esFiledInfo,esFiledInfoArr);
        }else {

            // 再判断是否是自定义实体类型
            if (!TypeOpt.checkEsType(esFiledInfo.getFieldType())){
                esFiledInfoArr = esFiledInfoArr == null ? new ArrayList<EsFiledInfo>():esFiledInfoArr;
                esFiledInfoArr.add(esFiledInfo);
                List<EsFiledInfo> esFiledInfos = pareFieldInfo(esFiledInfo.getFieldType(),esFiledInfoArr);
                esFiledInfo.setChildFields(esFiledInfos);
            }
        }
        return esFiledInfo;
    }

    /**
     * list 类型处理
     */
    public static void typeList(EsFiledInfo esFiledInfo, List<EsFiledInfo> esFiledInfoArr){
        String esFieldType = esFiledInfo.getEsFieldType();
        if (esFieldType!=null && !esFieldType.equals("object")){
            return;
        }
        Type fc = esFiledInfo.getField().getGenericType();
        Class typeGen = null;
        if (fc != null && fc instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) fc;
            Type[] types = pt.getActualTypeArguments();
            if (types.length == 1 && types[0] instanceof Class){
                typeGen = (Class) types[0];
            }
        }
        if (typeGen == null){
            typeGen =  Object.class;
        }else {
            if (!TypeOpt.checkEsType(typeGen)) {
                List<EsFiledInfo> esFiledInfoList = pareFieldInfo(typeGen,esFiledInfoArr);
                esFiledInfo.setChildFields(esFiledInfoList);
                typeGen = Object.class;
            }
        }
        esFiledInfo.setEsFieldType(TypeEnum.getTypeEnumByClass(typeGen).getEsType());
    }

    /**
     * 获取document 属性值
     * @param clazz
     * @param propertyName
     * @param <T>
     * @return
     */
    public static <T> T getDocumentProperty(Class clazz,String propertyName){
        Annotation[] annotations = clazz.getAnnotations();
        if (annotations == null || annotations.length == 0) return null;
        if (!clazz.isAnnotationPresent(ESDocument.class)) return null;
        Annotation annotation = clazz.getAnnotation(ESDocument.class);
        try {
            Method index_name = annotation.annotationType().getDeclaredMethod(propertyName);
            return (T) index_name.invoke(annotation);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}
